package org.cancerregulome.redi.websvc.rest;

import org.cancerregulome.redi.websvc.model.DatasetModel;
import org.cancerregulome.redi.websvc.model.DatasetModelsByIdMappingsHandler;
import org.json.JSONObject;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.systemsbiology.addama.commons.web.exceptions.ResourceNotFoundException;
import org.systemsbiology.addama.commons.web.views.JsonItemsView;
import org.systemsbiology.addama.jsonconfig.Mapping;
import org.systemsbiology.addama.jsonconfig.ServiceConfig;
import org.systemsbiology.google.visualization.datasource.jdbc.DatabaseTableColumnConnectionCallback;
import org.systemsbiology.google.visualization.datasource.jdbc.DatabaseTablesConnectionCallback;
import org.systemsbiology.google.visualization.datasource.jdbc.SimpleSqlDataTableGenerator;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

import static org.systemsbiology.addama.commons.web.utils.HttpIO.getURI;
import static org.systemsbiology.google.visualization.datasource.DataSourceHelper.executeDataSourceServletFlow;

/**
 * @author hrovira
 */
@Controller
public class DatasetsController implements InitializingBean {
    private static final Logger log = Logger.getLogger(DatasetsController.class.getName());

    private static final Map<String, Map<String, DatasetModel>> datasetModelsByProjectId = new HashMap<String, Map<String, DatasetModel>>();

    private ServiceConfig serviceConfig;

    public void setServiceConfig(ServiceConfig serviceConfig) {
        this.serviceConfig = serviceConfig;
    }

    public void afterPropertiesSet() throws Exception {
        this.serviceConfig.visit(new DatasetModelsByIdMappingsHandler(datasetModelsByProjectId));
    }

    /*
    * Controllers
    */
    @RequestMapping(value = "/**/datasets", method = RequestMethod.GET)
    protected ModelAndView datasets(HttpServletRequest request) throws Exception {
        String baseuri = getURI(request);
        log.info(baseuri);

        JSONObject json = new JSONObject();
        json.put("uri", baseuri);
        for (Mapping mapping : this.serviceConfig.getMappings()) {
            JSONObject item = new JSONObject();
            item.put("uri", baseuri + "/" + mapping.ID());
            item.put("label", mapping.LABEL());
            item.put("id", mapping.ID());
            json.append("items", item);
        }

        return new ModelAndView(new JsonItemsView()).addObject("json", json);
    }

    @RequestMapping(value = "/**/datasets/{projectId}", method = RequestMethod.GET)
    protected ModelAndView dataset(HttpServletRequest request,
                                   @PathVariable("projectId") String projectId) throws Exception {
        String baseuri = getURI(request);
        log.info(baseuri);

        Mapping m = serviceConfig.getMapping(projectId);
        if (m == null) {
            throw new ResourceNotFoundException(baseuri);
        }

        JSONObject json = new JSONObject();
        json.put("uri", baseuri);
        json.put("label", m.LABEL());
        json.put("id", m.ID());
        if (datasetModelsByProjectId.containsKey(projectId)) {
            Map<String, DatasetModel> datasetModels = datasetModelsByProjectId.get(projectId);
            for (DatasetModel datasetModel : datasetModels.values()) {
                JSONObject item = datasetModel.LiteJSON();
                item.put("uri", baseuri + "/" + datasetModel.ID());
                json.append("items", item);
            }
        }

        return new ModelAndView(new JsonItemsView()).addObject("json", json);
    }

    @RequestMapping(value = "/**/datasets/{projectId}/{datasetId}", method = RequestMethod.GET)
    protected ModelAndView item(HttpServletRequest request,
                                @PathVariable("projectId") String projectId,
                                @PathVariable("datasetId") String datasetId) throws Exception {
        String baseuri = getURI(request);
        log.info(baseuri + "[" + projectId + "," + datasetId + "]");

        if (datasetModelsByProjectId.containsKey(projectId)) {
            Map<String, DatasetModel> datasetModels = datasetModelsByProjectId.get(projectId);
            DatasetModel dm = datasetModels.get(datasetId);
            if (dm != null) {
                return new ModelAndView(new JsonItemsView()).addObject("json", dm.JSONwithURIs(baseuri));
            }
        }

        throw new ResourceNotFoundException(baseuri);
    }

    @RequestMapping(value = "/**/datasets/{projectId}/{datasetId}/datasources", method = RequestMethod.GET)
    protected ModelAndView datasources(HttpServletRequest request,
                                       @PathVariable("projectId") String projectId,
                                       @PathVariable("datasetId") String datasetId) throws Exception {
        String baseuri = getURI(request);
        log.info(baseuri + "[" + projectId + "," + datasetId + "]");

        JdbcTemplate jdbcTemplate = getJdbcTemplate(projectId, datasetId);
        JSONObject json = new JSONObject();
        for (String tableId : jdbcTemplate.execute(new DatabaseTablesConnectionCallback())) {
            JSONObject item = new JSONObject();
            item.put("id", tableId);
            item.put("uri", baseuri + "/" + tableId);
            json.append("items", item);
        }

        return new ModelAndView(new JsonItemsView()).addObject("json", json);
    }

    @RequestMapping(value = "/**/datasets/{projectId}/{datasetId}/datasources/{dataTableId}", method = RequestMethod.GET)
    protected ModelAndView datatable(HttpServletRequest request,
                                     @PathVariable("projectId") String projectId,
                                     @PathVariable("datasetId") String datasetId,
                                     @PathVariable("dataTableId") String dataTableId) throws Exception {
        String baseuri = getURI(request);
        log.info(baseuri + "[" + projectId + "," + datasetId + "," + dataTableId + "]");

        JdbcTemplate jdbcTemplate = getJdbcTemplate(projectId, datasetId);
        JSONObject json = jdbcTemplate.execute(new DatabaseTableColumnConnectionCallback(dataTableId));
        return new ModelAndView(new JsonItemsView()).addObject("json", json);
    }

    @RequestMapping(value = "/**/datasets/{projectId}/{datasetId}/datasources/{dataTableId}/query", method = RequestMethod.GET)
    protected void query(HttpServletRequest request, HttpServletResponse response,
                         @PathVariable("projectId") String projectId,
                         @PathVariable("datasetId") String datasetId,
                         @PathVariable("dataTableId") String dataTableId) throws Exception {
        String baseuri = getURI(request);
        log.info(baseuri + "[" + projectId + "," + datasetId + "," + dataTableId + "]");

        JdbcTemplate jdbcTemplate = getJdbcTemplate(projectId, datasetId);
        SimpleSqlDataTableGenerator tableGenerator = new SimpleSqlDataTableGenerator(jdbcTemplate, dataTableId);
        executeDataSourceServletFlow(request, response, tableGenerator);
    }

    private JdbcTemplate getJdbcTemplate(String projectId, String datasetId) throws ResourceNotFoundException {
        if (!datasetModelsByProjectId.containsKey(projectId)) {
            throw new ResourceNotFoundException(projectId);
        }

        Map<String, DatasetModel> datasetModelsById = datasetModelsByProjectId.get(projectId);
        if (!datasetModelsById.containsKey(datasetId)) {
            throw new ResourceNotFoundException(projectId + "/" + datasetId);
        }

        DatasetModel datasetModel = datasetModelsById.get(datasetId);
        JdbcTemplate jdbcTemplate = datasetModel.JDBC();
        if (jdbcTemplate == null) {
            throw new ResourceNotFoundException(projectId + "/" + datasetId + "/jdbc");
        }
        return jdbcTemplate;
    }
}
